<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <div name="app" id="app">

    </div>
    <div id="app">5555</div>
    <form action="111"></form>

    <ul id="ul"></ul>
    <script>
        // NodeList 对象是一个节点的集合，一般由 Node.childNodes 、 document.getElementsByName 和 document.querySelectorAll 返回的。
        // 不过需要注意， Node.childNodes 、 document.getElementsByName 返回的 NodeList 的结果是实时的（此时跟HTMLCollection比较类似），
        // 而 document.querySelectorAll 返回的结果是固定的，这一点比较特殊。
        const childNodes = document.body.childNodes
        console.log(childNodes.length, 111);
        document.body.appendChild(document.createElement('div'))
        console.log(childNodes.length, 111);

        // HTMLCollection ,HTMLCollection是一个特殊的NodeList，表示包含了若干元素（元素顺序为文档流中的顺序）的通用集合，它是实时更新的，

        // 节点查找API
        // 1. document.getElementById ：根据ID查找元素，大小写敏感，如果有多个结果，只返回第一个；
        let app = document.getElementById('app')
        // 2. document.getElementsByTagName ：根据标签查找元素， * 表示查询所有标签，返回一个 HTMLCollection 。
        let tag = document.getElementsByTagName('div')
        // 3. document.getElementsByName ：根据元素的name属性查找，返回一个 NodeList 。
        let name = document.getElementsByName('app')
        // 4. document.querySelector ：返回单个Node，IE8 + (含），如果匹配到多个结果，只返回第一个。
        let query = document.querySelector('#app')
        // 5. document.querySelectorAll ：返回一个 NodeList ，IE8 + (含）。
        let queryAll = document.querySelectorAll('#app')
        // 6. document.forms ：获取当前页面所有form，返回一个 HTMLCollection ；
        let form = document.forms[0]


        // 节点创建API
        // 节点创建API主要包括 createElement 、 createTextNode 、 cloneNode 和 createDocumentFragment 四个方法。
        // createElement创建元素
        let elem = document.createElement('div')
        elem.id = 'test'
        elem.style = 'color:red'
        elem.innerHTML = '我是新创建的节点'
        document.body.appendChild(elem)
        // createTextNode 创建文本节点：
        var node = document.createTextNode("我是文本节点");
        document.body.appendChild(node);
        // cloneNode 克隆一个节点： node.cloneNode(true/false) ，它接收一个bool参数，用来表示是否复制子元素。
        var from = document.getElementById("test");
        var clone = from.cloneNode(true);
        clone.id = "test2";
        document.body.appendChild(clone);
        // createDocumentFragment
        // 本方法用来创建一个 DocumentFragment ，也就是文档碎片，它表示一种轻量级的文档，主要是用来存储临时节点，大量操作DOM时用它可以大大提升性能。
        
        // 假设现有一题目，要求给ul添加10000个li，我们先用最简单的拼接字符串的方式来实现：
        (function () {
            var start = Date.now();
            var str = '';
            for (var i = 0; i < 10000; i++) {
                str += '<li>第' + i + '个子节点</li>';
            }
            document.getElementById('ul').innerHTML = str;
            console.log('耗时：' + (Date.now() - start) + '毫秒'); // 17毫秒
        })()
        // 再换逐个append的方式，不用说，这种方式效率肯定低：
        (function () {
            var start = Date.now();
            var ul = document.getElementById('ul');
            for (var i = 0; i < 10000; i++) {
                li = document.createElement('li');
                li.textContent = '第' + i + '个子节点';
                ul.appendChild(li);
            }
            console.log('耗时：' + (Date.now() - start) + '毫秒'); // 66毫秒
        })()
        // 文档碎片的方法, 比第二种耗时更短
        (function () {
            var start = Date.now()
            var ul = document.getElementById('ul')
            var fragment = document.createDocumentFragment()
            for (var i = 0; i < 10000; i++) {
                li = document.createElement('li');
                li.textContent = '第' + i + '个子节点';
                fragment.appendChild(li);
            }
            ul.appendChild(fragment);
            console.log('耗时：' + (Date.now() - start) + '毫秒')
        })()



        // 节点修改API
        // 节点修改API都具有下面这几个特点：
        // 不管是新增还是替换节点，如果其原本就在页面上，那么原来位置的节点将被移除；
        // 修改之后节点本身绑定的事件不会消失；

        // appendChild
        parent.appendChild(child);
        // 它会将child追加到parent的子节点的最后面。另外，如果被添加的节点是一个页面中存在的节点，则执行后这个节点将会添加到新的位置，
        // 其原本所在的位置将移除该节点，也就是说不会同时存在两个该节点在页面上，且其事件会保留。

        // insertBefore将某个节点插入到另外一个节点的前面

        // removeChild removeChild用于删除指定的子节点并返回子节点
        var deletedChild = parent.removeChild(node);

        // deletedChild指向被删除节点的引用，它仍然存在于内存中，可以对其进行下一步操作。
        // 另外，如果被删除的节点不是其子节点，则将会报错。一般删除节点都是这么删的：
        function removeNode(node) {
            if (!node) return;
            if (node.parentNode) node.parentNode.removeChild(node);
        }

        // replaceChild
        // replaceChild用于将一个节点替换另一个节点，语法：
        parent.replaceChild(newChild, oldChild);


        // 节点关系API
        // 父关系API
        // parentNode ：每个节点都有一个parentNode属性，它表示元素的父节点。Element的父节点可能是Element，Document或DocumentFragment；
        // parentElement ：返回元素的父元素节点，与parentNode的区别在于，其父节点必须是一个Element元素，如果不是，则返回null；

        // 子关系API
        // children ：返回一个实时的 HTMLCollection ，子节点都是Element，IE9以下浏览器不支持；
        // childNodes ：返回一个实时的 NodeList ，表示元素的子节点列表，注意子节点可能包含文本节点、注释节点等；
        // firstChild ：返回第一个子节点，不存在返回null，与之相对应的还有一个 firstElementChild ；
        // lastChild ：返回最后一个子节点，不存在返回null，与之相对应的还有一个 lastElementChild ；


        // 兄弟关系型API
        // previousSibling ：节点的前一个节点，如果不存在则返回null。注意有可能拿到的节点是文本节点或注释节点，与预期的不符，要进行处理一下。
        // nextSibling ：节点的后一个节点，如果不存在则返回null。注意有可能拿到的节点是文本节点，与预期的不符，要进行处理一下。
        // previousElementSibling ：返回前一个元素节点，前一个节点必须是Element，注意IE9以下浏览器不支持。
        // nextElementSibling ：返回后一个元素节点，后一个节点必须是Element，注意IE9以下浏览器不支持。

        // 元素属性型API
        // setAttribute
        element.setAttribute(name, value);
        // getAttribute
        // getAttribute返回指定的特性名相应的特性值，如果不存在，则返回null：
        var value = element.getAttribute("id");


        // 样式相关API
        // 直接修改元素的样式
        elem.style.color = 'green';
        elem.style.setProperty('font-size', '50px');
        elem.style.removeProperty('color');
        // 动态添加样式规则
        var style = document.createElement('style');
        style.innerHTML = 'body{color:red} #top:hover{background-color: red;color: white;}';
        document.head.appendChild(style);

        
        // window.getComputedStyle
        // 通过 element.sytle.xxx 只能获取到内联样式，借助 window.getComputedStyle 可以获取应用到元素上的所有样式，IE8或更低版本不支持此方法。
        var style = window.getComputedStyle(element[pseudoElt]);

        // getBoundingClientRect
        // getBoundingClientRect 用来返回元素的大小以及相对于浏览器可视窗口的位置，用法如下：
        var clientRect = element.getBoundingClientRect();
        
    </script>
</body>

</html>